package ratelimit.single;



import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.util.WeakHashMap;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @Auther: Fazhan.Qiao
 * @Date: 2019/5/27 14:59
 * @Description: 计数器限流，波动较大，不够平滑；不能应对突发的流量；
 *
 *主要用来限制总并发数，比如数据库连接池大小、线程池大小、程序访问并发数等都是使用计数器算法。
 *
 *  * 可以针对某个单机应用进行请求总量的限流，用在Filter中；
 *  * 也可以针对某个方法进行限流，如用在Controller、service中；
 *  * demo()方法演示了如何使用。mian方法是一些测试
 *  *
 *  * 不同业务的限流名称不要相同，名称相同会使用同一个限流实例；
 */
public class CountLimitFactory {

    private static WeakHashMap<String, Semaphore> limiterMap = new WeakHashMap<>();
    private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public static Semaphore getLock(String keyName, int limit) {//限流后阻塞（排队等待）
        Semaphore semaphore=null;
        try {
            lock.readLock().lock();
            if (limiterMap.containsKey(keyName)) {
                semaphore = limiterMap.get(keyName);
            }
        } catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.readLock().unlock();
        }

        if (semaphore == null) {
            try {
                lock.writeLock().lock();
                if (limiterMap.containsKey(keyName)) {//双重检查
                    semaphore = limiterMap.get(keyName);
                }
                if (semaphore == null) {
                    semaphore = new Semaphore(limit);
                    limiterMap.put(keyName, semaphore);
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                lock.writeLock().unlock();
            }
        }
        return semaphore;
    }


/*    public static void demo1() {
        //限流每秒500
        Semaphore lock=CountLimitFactory.getLock("user.query",500);
      try {
            lock.acquire();//获取不到锁时，会一直阻塞；请求排队等待；
            // 处理业务逻辑
            System.out.println("--" + System.currentTimeMillis() / 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.release();//业务处理完后，必须释放锁
        }

         // 处理核心逻辑

    }*/



/*    public static void demo2() {
        //限流每秒11
       int limit=11;
        Semaphore lock=CountLimitFactory.getLock("order.query",11);
        try {
            boolean nolimit= lock.tryAcquire(1000 * 1000 * 1000 - 1000 * 1000 * 1000 / limit,TimeUnit.NANOSECONDS);  //规定时间内获取不到锁时，会直接返回false；
            if(nolimit){//获取到锁了
                // 处理核心逻辑
                System.out.println("--" + System.currentTimeMillis() / 1000);
            }else{
                //没有获取到锁 限流了，直接丢弃请求，抛出异常
                 throw new RuntimeException("当前请求过多，请稍后再试");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.release();//业务处理完后，必须释放锁
        }
    }*/


    public static void main(String[] args) {
        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("countlimit-%s").build();

        ExecutorService threadPoolExecutor = new ThreadPoolExecutor(5, 200, 30L, TimeUnit.MINUTES, new ArrayBlockingQueue<Runnable>(50),threadFactory);

//        threadPoolExecutor.execute(new Runnable() {
//            @Override
//            public void run() {
//                for (int i = 0; i < 50; i++) {
//                    System.out.println("1111");
//                }
//            }
//        });

     /*   for (int i = 0; i < 200; i++) {
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    //并发200个请求，没获取到锁的请求会阻塞（排队等待重试）
                    //限流每秒12
                    Semaphore lock=CountLimitFactory.getLock("print2",12);
                    try {
                        lock.acquire();//获取不到锁时，会一直阻塞；请求排队等待；
                        // 处理业务逻辑
                        System.out.println("22222222   :");
                        TimeUnit.MILLISECONDS.sleep(300);//模拟业务耗时操作，0.3S；
                        // 如果业务执行很快，限流效果是看不出来的。这种业务执行很快的场景，一般也不需要限流。
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.release();//业务处理完后，必须释放锁！！！！
                    }
                }
            });
        }
*/

        for (int i = 0; i < 100; i++) {
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    //并发100个请求，
                    int limit=11;
                    Semaphore lock=CountLimitFactory.getLock("print3",11);
                    try {
                        boolean nolimit= lock.tryAcquire();  //规定时间内获取不到锁时，会直接返回false；
                        if(nolimit){//获取到锁了
                            // 处理核心逻辑
                            System.out.println("33333333333333   :");
                            TimeUnit.MILLISECONDS.sleep(300);//模拟业务耗时操作，0.3S；
                            // 如果业务执行很快，限流效果是看不出来的。这种业务执行很快的场景，一般也不需要限流。
                        }else{
                            //没有获取到锁 限流了，直接丢弃请求，抛出异常
                            throw new RuntimeException("当前请求过多，请稍后再试");
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.release();//业务处理完后，必须释放锁
                    }
                }
            });
        }

    }
}
